package com.gdgk.shop.security.api.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gdgk.shop.bean.model.User;
import com.gdgk.shop.common.exception.GdgkShopBindException;
import com.gdgk.shop.common.response.ResponseEnum;
import com.gdgk.shop.common.util.PrincipalUtil;
import com.gdgk.shop.dao.UserMapper;
import com.gdgk.shop.security.api.enumeration.LoginType;
import com.gdgk.shop.security.api.model.WxUser;
import com.gdgk.shop.security.api.properties.WxMiniappProperties;
import com.gdgk.shop.security.common.bo.UserInfoInTokenBO;
import com.gdgk.shop.security.common.dto.AuthenticationDTO;
import com.gdgk.shop.security.common.enums.SysTypeEnum;
import com.gdgk.shop.security.common.manager.PasswordCheckManager;
import com.gdgk.shop.security.common.manager.PasswordManager;
import com.gdgk.shop.security.common.manager.TokenStore;
import com.gdgk.shop.security.common.vo.TokenInfoVO;
import com.gdgk.shop.security.common.dto.WxAuthenticationDTO;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import com.gdgk.shop.common.response.ServerResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 菠萝凤梨
 * @date 2022/3/28 15:20
 */
@RestController
@Tag(name = "登录")
public class LoginController {
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordCheckManager passwordCheckManager;

    @Autowired
    private PasswordManager passwordManager;

    @Autowired
    private WxMiniappProperties wxMiniappProperties;

    @PostMapping("/login")
    @Operation(summary = "账号密码(用于前端登录)", description = "通过账号/手机号/用户名密码登录，还要携带用户的类型，也就是用户所在的系统")
    public ServerResponseEntity<TokenInfoVO> login(
            @Valid @RequestBody AuthenticationDTO authenticationDTO) {
        String mobileOrUserName = authenticationDTO.getUserName();
        User user = getUser(mobileOrUserName);

        String decryptPassword = passwordManager.decryptPassword(authenticationDTO.getPassWord());

        // 半小时内密码输入错误十次，已限制登录30分钟
        passwordCheckManager.checkPassword(SysTypeEnum.ORDINARY, authenticationDTO.getUserName(), decryptPassword, user.getLoginPassword());

        UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
        userInfoInToken.setUserId(user.getUserId());
        userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
        userInfoInToken.setEnabled(user.getStatus() == 1);
        // 存储token返回vo
        TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
        return ServerResponseEntity.success(tokenInfoVO);
    }

    @PostMapping("/wxLogin")
    @Operation(summary = "微信小程序用户登录", description = "微信小程序用户登录")
    public ServerResponseEntity<TokenInfoVO> wxLogin(@Valid @RequestBody WxAuthenticationDTO wxAuthenticationDTO) {
        // 通过登录凭证校验接口获取session_key和openid
        Map<String, Object> map = new HashMap<>();
        map.put("appid", wxMiniappProperties.getAppId());
        map.put("secret", wxMiniappProperties.getSecret());
        map.put("js_code", wxAuthenticationDTO.getCode());
        map.put("grant_type", "authorization_code");
        // 向微信服务器发出请求
        String resp = HttpUtil.get(wxMiniappProperties.getLoginApi(), map);
        WxUser wxUser = JSONUtil.toBean(resp, WxUser.class);

        if (wxUser.getOpenid() == null || wxUser.getOpenid().isEmpty()) {
            throw new GdgkShopBindException(ResponseEnum.SHOW_FAIL, "用户注册失败!");
        }

        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, wxUser.getOpenid()));
        UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
        //注册
        if (user == null) {
            User createUser = new User();
            Date now = new Date();
            createUser.setUserId(IdUtil.simpleUUID());
            createUser.setPic(wxAuthenticationDTO.getAvatarUrl());
            createUser.setModifyTime(now);
            createUser.setUserRegtime(now);
            createUser.setLoginType(LoginType.WX.getValue());
            createUser.setNickName(wxAuthenticationDTO.getNickName());
            createUser.setUserMobile(wxAuthenticationDTO.getCodeMobile());
            createUser.setOpenId(wxUser.getOpenid());
            int status = userMapper.insert(createUser);
            if (status > 0) {
                userInfoInToken.setUserId(createUser.getUserId());
                userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
                userInfoInToken.setEnabled(true);
                userInfoInToken.setWxOpenId(createUser.getOpenId());
                TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
                return ServerResponseEntity.success(tokenInfoVO);
            } else {
                throw new GdgkShopBindException(ResponseEnum.SHOW_FAIL, "用户注册失败!");
            }
        } else {
            //登录
            userInfoInToken.setUserId(user.getUserId());
            userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
            userInfoInToken.setEnabled(true);
            userInfoInToken.setWxOpenId(user.getOpenId());
            TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
            User userQueryByOpenId = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, user.getOpenId()));
            tokenInfoVO.setBindStatus(userQueryByOpenId.getUserMobile() != null);
            return ServerResponseEntity.success(tokenInfoVO);
        }
    }

    private User getUser(String mobileOrUserName) {
        User user = null;
        // 手机验证码登陆，或传过来的账号很像手机号
        if (PrincipalUtil.isMobile(mobileOrUserName)) {
            user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, mobileOrUserName));
        }
        // 如果不是手机验证码登陆， 找不到手机号就找用户名
        if (user == null) {
            user = userMapper.selectOneByUserName(mobileOrUserName);
        }
        if (user == null) {
            throw new GdgkShopBindException("账号或密码不正确");
        }
        return user;
    }
}
